/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_kernel.htm
 *
 * Zhiqim Kernel is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.kernel;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarEntry;

import org.zhiqim.kernel.constants.ZhiqimConstants;
import org.zhiqim.kernel.model.FilterHandler;
import org.zhiqim.kernel.model.Filters;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Jars;
import org.zhiqim.kernel.util.Lists;
import org.zhiqim.kernel.util.Resources;
import org.zhiqim.kernel.util.Validates;

/**
 * 工程服务对应的ClassLoader
 *
 * @version v1.5.2 @author zouzhigang 2021年6月21日 新建与整理
 */
public final class ZhiqimClassServiceLoader extends URLClassLoader implements ZhiqimConstants
{
    private final String serviceId;
    private final List<String> classpath;
    private final List<String> aliasList;

    public ZhiqimClassServiceLoader(ClassLoader parent, String serviceId, String serviceLoaderValue)
    {
        super(new URL[0], parent);
        
        this.serviceId = serviceId;
        this.classpath = Lists.toStringList(serviceLoaderValue);
        this.aliasList = new ArrayList<>();
    }

    /************************************************************************************************/
    //开启&关闭
    /************************************************************************************************/

    /** 开启 */
    boolean open() throws Exception
    {
        //1.加载JAR
        for (String filePath : classpath)
        {
            File item = new File(filePath);
            if (!item.exists() || !item.isFile() || !item.canRead())
            {
                throw Asserts.exception("文件[%s]不存在", filePath);
            }

            addURL(item.toURI().toURL());
        }
        
        //2.加载别名时设置为本ClassLoader
        ClassLoader currLoader = Thread.currentThread().getContextClassLoader();
        
        try
        {
            Thread.currentThread().setContextClassLoader(this);
            return Resources.scanClassPath(Filters.CLASS, new LoadAliasHander(), classpath);
        }
        finally
        {
            Thread.currentThread().setContextClassLoader(currLoader);
        }
    }
    
    @Override
    public void close() throws IOException 
    {
        //1.清除别名
        for (String alias : aliasList)
        {
            Z.cls().remove(alias);
        }
        aliasList.clear();
        
        //2.关闭加载器
        super.close();
    }
    
    @Override
    public String toString()
    {
        return serviceId + "-ClassLoader";
    }
    
    /** 加载别名的过滤处理器 */
    private class LoadAliasHander implements FilterHandler
    {
        @Override
        public boolean handle(Object obj) throws Exception
        {
            String className = null;
            Object[] objs = (Object[])obj;
            if (objs[0] instanceof File)
            {
                File classPath = (File)objs[0];
                File classFile = (File)objs[1];
                className = Files.getClassName(classPath, classFile);
            }
            else
            {
                JarEntry jarEntry = (JarEntry)objs[1];
                className = Jars.getClassName(jarEntry);
            }
            
            String alias = Zhiqim.addClassAlias(className);
            if (alias == null)
                return false;
            
            if (Validates.isNotEmpty(alias))
                aliasList.add(alias);
            return true;
        }
    }
}
